﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using MBT.Modeling;

namespace MBT.Modeling
{
    /// <summary>
    /// A Test Model (keeping the information of the model during the run time). A test model can be composed of several sub-models
    /// </summary>
    public class TestModel
    {
        //#region -=[ Internal Structure ]=-
        ///// <summary>
        ///// This would be usefull when we have several functions for ONE action and they should be sync
        ///// </summary>
        //class testAction1
        //{
        //    //string id;
        //    public List<TestEvent> events = new List<TestEvent>();

        //    //public string ID
        //    //{
        //    //    get
        //    //    {
        //    //        return id;
        //    //    }
        //    //}

        //    public void GenerateDomain()
        //    {

        //    }
        //    public bool PreCondition(int instanceID)
        //    {

        //        return false;
        //    }
        //    public void Execute(int instanceID)
        //    {

        //    }
        //}
        //#endregion

        List<AtomicTestModel> subModels = new List<AtomicTestModel>();
        /// <summary>
        /// List of executable submodels
        /// </summary>
        List<object> executableModelObjects = new List<object>();

        /// <summary>
        /// Represent the type of "state"
        /// </summary>
        Type stateType;


        /// <summary>
        /// Construcor
        /// </summary>
        /// <param name="models">atomic models</param>
        public TestModel(AtomicTestModel[] models)
        {
            for (int i = 0; i < models.Length; i++)
            {
                this.subModels.Add(models[i]);
                executableModelObjects.Add(models[i].ExecutableObject);
                foreach (TestEvent e in models[i].Events)
                {
                    this.Events.Add(e);
                    //testAction action = new testAction();
                    //action.events.Add(e);
                    //actionVocab.Add(action);
                }
            }
        }

        /// <summary>
        /// Set the Type of state
        /// <remarks>Note that the type of state should have a default constructor (i.e. with no parameter). Here, we make the class
        /// ourself and we are sure that this is the case for the class but it should be the case also for its elements!
        /// </remarks>
        /// </summary>
        /// <param name="stateType">Type of state</param>
        public void SetStateType(Type stateType)
        {
            this.stateType = stateType;
        }

        /// <summary>
        /// List of all events
        /// </summary>
        public List<TestEvent> Events
        {
            get
            {
                List<TestEvent> ev = new List<TestEvent>();
                foreach (AtomicTestModel a in subModels)
                {
                    foreach (TestEvent e in a.Events)
                        ev.Add(e);
                }
                return ev;
            }
        }


        #region Run-Time Methods
        bool executeDirectly = true;

        /// <summary>
        /// Get current state of the model
        /// </summary>
        /// <returns>the current state</returns>
        public object GetCurrentState()
        {
            if (executeDirectly)
            {
                object currState = Activator.CreateInstance(stateType, executableModelObjects.ToArray());
                return currState;
            }
            else
                return null;// ExecutableObject.GetType().InvokeMember("CurrentState", BindingFlags.GetProperty, null, ExecutableObject, null);
        }

        /// <summary>
        /// Set the current state of the model
        /// </summary>
        /// <param name="st">the state that should be set as current</param>
        public void SetCurrentState(object st)
        {
            if (executeDirectly)
            {
                stateType.InvokeMember("CloneTo", BindingFlags.InvokeMethod, null, st, executableModelObjects.ToArray());
            }
            else
            {
                //ExecutableObject.GetType().InvokeMember("CurrentState", BindingFlags.SetProperty, null, ExecutableObject, new object[] { st });
            }
        }

        /// <summary>
        /// Validate the current state
        /// </summary>
        public void ValidateCurrentState()
        {
            //if (!executeDirectly)
            //{
            //    ExecutableObject.GetType().InvokeMember(
            //                                "ValidateCurrentState", BindingFlags.InvokeMethod,
            //                                     null, ExecutableObject, null);
            //}
            //else
            //{
            //    //(bool)
            //    //((ExecFunction)slicer.SkipState).info.Invoke(ExecutableObject, BindingFlags.InvokeMethod, null, null,
            //    //    System.Globalization.CultureInfo.CurrentCulture);
            //}
        }
        
        /// <summary>
        /// Check wheather the current state is final or not
        /// </summary>
        /// <returns></returns>
        public bool isFinalState()
        {
            return false;
            //if (!executeDirectly)
            //{
            //    return (bool)ExecutableObject.GetType().InvokeMember("isFinalState", BindingFlags.InvokeMethod, null, ExecutableObject, null);
            //}
            //else
            //{
            //    return true;
            //}
        }

        /// <summary>
        /// Check whether we should skip the current state or not
        /// </summary>
        /// <returns></returns>
        public bool SkipCurrentState()
        {
            return false;
            //if (!executeDirectly)
            //{
            //    return (bool)ExecutableObject.GetType().InvokeMember(
            //                            "skipState", BindingFlags.InvokeMethod,
            //                                 null, ExecutableObject, null);
            //}
            //else
            //{
            //    return false;
            //}
        }

        //List<object> domains;
        //public int GenerateDomains(testAction e)
        //{
        //    if (!executeDirectly)
        //    {
        //        return e.RunNumberOfActionInstancesHandler();
        //    }

        //    domains = new List<object>();
        //    int size = 1;
        //    for (int i = 0; i < e.NumberOfParams; i++)
        //    {
        //        string funcName;
        //        string domianID;
        //        ((ExecFunction)e.ActionAttrib).paramDomains.TryGetValue(((ExecFunction)e.ActionAttrib).parameters[i].ID, out domianID);
        //        slicer.domainFunctions.TryGetValue(domianID, out funcName);
        //        //Type paramType = e.exeInfo.GetParameters()[i].GetType();
        //        object dom = typeof(ExampleModel).
        //            InvokeMember(funcName, BindingFlags.InvokeMethod, null, ExecutableObject, null);
        //        System.Collections.IList collection = dom as System.Collections.IList;
        //        size *= collection.Count;
        //        domains.Add(collection);
        //    }
        //    return size;
        //}

        //public void RunActionHandler(EventsCollection4Test.TestEvent e, int instanceID)
        //{
        //    if (!executeDirectly)
        //    {
        //        e.RunActionHandler(instanceID);
        //        return;
        //    }

        //    List<object> parameters = new List<object>();
        //    for (int i = 0; i < domains.Count; i++)
        //    {
        //        System.Collections.IList collection = domains[i] as System.Collections.IList;
        //        parameters.Add(collection[instanceID % collection.Count]);
        //        instanceID /= collection.Count;

        //    }
        //    e.exeInfo.Invoke(ExecutableObject, BindingFlags.InvokeMethod, null, parameters.ToArray(),
        //        System.Globalization.CultureInfo.CurrentCulture);
        //}
        //public bool RunPreconditionHandler(EventsCollection4Test.TestEvent e, int instanceID)
        //{
        //    if (!executeDirectly)
        //    {
        //        return e.RunPreconditionHandler(instanceID);
        //    }
        //    List<object> parameters = new List<object>();
        //    for (int i = 0; i < domains.Count; i++)
        //    {
        //        System.Collections.IList collection = domains[i] as System.Collections.IList;
        //        parameters.Add(collection[instanceID % collection.Count]);
        //        instanceID /= collection.Count;
        //    }
        //    return (bool)e.preInfo.Invoke(ExecutableObject, BindingFlags.InvokeMethod, null, parameters.ToArray(),
        //        System.Globalization.CultureInfo.CurrentCulture);
        //}
        /////////////////////////////////////////////This part should be done for each of executable objects!//////////////////
        //public void ValidateCurrentState()
        //{
        //    if (!executeDirectly)
        //    {
        //        //ExecutableObject.GetType().InvokeMember(
        //        //                            "ValidateCurrentState", BindingFlags.InvokeMethod,
        //        //                                 null, ExecutableObject, null);
        //    }
        //    else
        //    {
        //        //(bool)
        //        //((ExecFunction)slicer.SkipState).info.Invoke(ExecutableObject, BindingFlags.InvokeMethod, null, null,
        //        //    System.Globalization.CultureInfo.CurrentCulture);
        //    }
        //}
        //public bool isFinalState()
        //{
        //    if (!executeDirectly)
        //    {
        //        return (bool)false;
        //        //ExecutableObject.GetType().InvokeMember("isFinalState", BindingFlags.InvokeMethod, null, ExecutableObject, null);
        //    }
        //    else
        //    {
        //        return true;
        //    }
        //}
        //public bool SkipCurrentState()
        //{
        //    if (!executeDirectly)
        //    {
        //        return (bool)false;
        //        //ExecutableObject.GetType().InvokeMember("skipState", BindingFlags.InvokeMethod, null, ExecutableObject, null);
        //    }
        //    else
        //    {
        //        return false;
        //    }
        //}
        #endregion
    }
}
